/** PettyXML - A very basic xml parser
  * Copyright (c) 2008 Philipp Gildein (rmbl@openspeak-project.org)
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *
  *    * Redistributions of source code must retain the above copyright notice,
  *         this list of conditions and the following disclaimer.
  *    * Redistributions in binary form must reproduce the above copyright notice,
  *         this list of conditions and the following disclaimer in the documentation
  *         and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */

#ifndef __PETTYXML_HPP__
#define __PETTYXML_HPP__

#include <string>
#include <fstream>
#include <map>
#include <vector>
#include <exception>

namespace PettyXML
{

    /** \class XMLException
     *  \brief A very basic exception class
     *
     *  The XMLException class provides just one method except of the
     *  (de-)constructors and allows the user to easily get the error
     *  message after it got thrown by a part of NanoXML.
     */
    class XMLException : public std::exception
    {
     public:
        /** \brief The constructor of the XMLException class
         *  \param msg The error message
         */
        XMLException(const std::string &msg) : mMsg(msg) { }

        /** \brief The destructor of the XMLException class */
        virtual ~XMLException(void) throw()  { }

        /** \brief Returnes the error messages
         *  \return The error message
         */
        virtual const char* what(void) const throw() 
        {
            return mMsg.c_str();
        }

     protected:
        std::string mMsg;   /**< The error message */
    };


    /** \class XMLNode
     *  \brief Contains all properties of a specific node in a xml document
     *
     *  The XMLNode represents one specific node in a xml document.
     *  It can parse its own node including all attributes and children and
     *  can write them back to the file.
     *  The public methods allow easy access to all attributes and children
     *  and allows to change them or add new ones.
     */
    class XMLNode
    {
     public:
        /** \brief The Type of the node */
        enum Type
        {
            UNKNOWN = 0,
            ELEMENT,
            COMMENT,
            DEFINITION,
            DTD,
            TEXT
        };

     public:
        typedef std::map<std::string, std::string> AttributesMap;
        typedef std::vector<XMLNode*> ChildrenVector;
        typedef ChildrenVector::const_iterator  const_iterator;
        typedef ChildrenVector::iterator        iterator;

        friend class XMLDocument;

        /** \brief The constructor of the XMLNode class */
        XMLNode(void);

        /** \brief The constructor of the XMLNode class
         *  \param name The name of the node
         */
        XMLNode(const std::string &name);

        /** \brief The destructor of the XMLNode class */
        virtual ~XMLNode(void);

        /** \brief Set the value of an attribute
         *  \param name The name of the attribute to change
         *  \param value The new value of the attribute
         */
        void setAttribute(const std::string &name, const std::string &value);

        /** \brief Get the value of an attribute
         *  \param name The name of the attribute
         *  \return The value of the attribute
         */
        std::string getAttribute(const std::string &name) const;

        /** \brief Check if an attribute exists
         *  \param name The name of the attribute
         *  \return True if it does exist
         */
        bool attributeExists(const std::string &name) const;

        /** \brief Set the name of the node
         *  \param name The new name of the node
         */
        inline void setName(const std::string &name)
        {
            mName = name;
        }

        /** \brief Get the name of the node
         *  \return The name of the node
         */
        inline std::string getName(void) const
        {
            return mName;
        }

        /** \brief Set the text inside of the starting and closing tags
         *  \param text The text to use
         */
        void setText(const std::string &text);

        /** \brief Get the text inside of the tag
         *  \return The text inside of the tag
         */
        inline std::string getText(void) const
        {
            return mText;
        }

        /** \brief Get the %Type of the node
         *  \return The %Type of the node
         */
        inline Type getType(void) const
        {
            return mType;
        }

        /** \brief Add a child tag to the node
         *  \param child The child to add
         */
        void addChild(XMLNode *child);

        /** \brief Remove a child from the node
         *  \param child The child to remove
         */
        void removeChild(XMLNode *child);

        /** \brief Remove a child from the node
         *  \param index The index (0..?) of the child
         */
        void removeChild(const unsigned int &index);

        /** \brief Get a pointer to a child
         *  \param index The index (0..?) of the child
         *  \return The child or 0
         */
        inline XMLNode* getChild(const unsigned int &index) const
        {
            return index < mChildren.size() ? mChildren[index] : 0;
        }

        /** \brief Get the first child in the vector
         *  \return The first child in the vector or 0
         */
        inline XMLNode* getFirstChild(void) const
        {
            return mChildren.size() ? mChildren[0] : 0;
        }

        /** \brief Find a specific child
         *  \param name The name of the child
         *  \return The child or 0
         */
        XMLNode* findChild(const std::string &name) const;

        /** \brief Return an iterator to the beginning of the vector
         *  \return The iterator to the beginning of the vector
         */
        iterator begin(void)
        {
            return mChildren.begin();
        }

        /** \brief Return a const_iterator to the beginning of the vector
         *  \return The const_iterator to the beginning of the vector
         */
        const_iterator begin(void) const
        {
            return mChildren.begin();
        }

        /** \brief Return an iterator to the end of the vector
         *  \return The iterator to the end of the vector
         */
        iterator end(void)
        {
            return mChildren.end();
        }

        /** \brief Return a const_iterator to the end of the vector
         *  \return The const_iterator to the end of the vector
         */
        const_iterator end(void) const
        {
            return mChildren.end();
        }

     protected:
        /** \brief Parse a part of an xml file
         *  \param restOfFile The rest of the file to parse
         *  \return True if it got parsed successfully
         */
        bool parse(std::string &restOfFile);

        /** \brief Print the node to the given file
         *  \param file The file to write to
         *  \param col The column this node is in
         */
        void printToFile(std::ostream &file, const unsigned int &col) const;

        /** \brief Try to identify a tag by its beginning
         *  \param tag The tag to identify
         *  \return The %Type of the tag
         */
        Type identify(const std::string &tag) const;

        /** \brief Parse a given tag
         *  \param tag The tag to parse
         *  \return The %Type of the tag
         */
        Type parseTag(const std::string &tag);

        AttributesMap   mAttributes; /**< Map with all attributes of the node */
        ChildrenVector  mChildren;   /**< Vector with all children of the node */
        std::string     mName;       /**< The name of the node */
        std::string     mText;       /**< The text inside of the node if there is any */
        Type            mType;       /**< The %Type of the node */
        bool            mClosed;     /**< Has the parser found the closing tag already? */

    };


    /** \class XMLDocument
     *  \brief The basic xml document
     *
     *  The XMLDocument class represents a xml file and interacts with it.
     *  It also inherits the XMLNode class to allow access to the root
     *  nodes of this document and the possibility to change them or add
     *  new ones.
     */
    class XMLDocument : public XMLNode
    {
     public:
        /** \brief The constructor of the XMLDocument class
         *  \param file The xml file to read/write from/to
         */
        XMLDocument(const std::string &file);

        /** \brief The constructor of the XMLDocument class */
        XMLDocument(void);

        /** \brief The destructor of the XMLDocument class */
        virtual ~XMLDocument(void);

        /** \brief Change the name of the xml file
         *  \param file The new filename
         */
        void setFile(const std::string &file) { mFile = file; }

        /** \brief Parse the contents of the file
         *  \return True if the parsing is successfull
         */
        bool parse(void);

        /** \brief Parse the contents of the string
         *  \param xml The xml string
         *  \return True if the parsing is successfull
         */
        bool parse(const std::string &xml);

        /** \brief Save everything back to the file
         *  \return True if everything got saved successfully
         */
        bool save(void);

        /** \brief Save everything to a stream
         *  \param out The stream to save to
         *  \return True if everything got saved successfully
         */
        bool save(std::ostream &out);

     protected:
        std::string     mFile;      /**< The name of the file to read/write from/to */
    };
    
}

#endif
